85장. 컨테이너 로깅 — FireLens · awslogs 드라이버
이 장에서 말하고자 하는 것
컨테이너의 표준 로그 방식은
표준 출력(stdout) / 표준 에러(stderr) 에 쓴다
다.
이 출력을 어떻게 CloudWatch (또는 다른 백엔드) 로 보낼지가
컨테이너 로깅 드라이버 의 일이다.
ECS에서는 두 가지가 흔하다.
awslogs드라이버- FireLens (Fluent Bit / Fluentd 사이드카)
1. awslogs 드라이버 — 가장 간단
ECS Task Definition에 다음을 추가하면 끝.
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/orders",
"awslogs-region": "ap-northeast-2",
"awslogs-stream-prefix": "app"
}
}
ECS 에이전트가 컨테이너의 stdout/stderr를 CloudWatch Logs로 직접 보낸다.
- 설정이 단순
- 컨테이너 이미지에 추가 사이드카 필요 없음
- 보낼 곳을 CloudWatch 외에 못 정함
단순한 서비스는 거의 항상 awslogs로 충분
2. FireLens — 더 유연한 로깅 사이드카
ECS Task에 Fluent Bit (또는 Fluentd) 사이드카 를 띄워
로그를 다양한 백엔드로 보낸다.
[app container] → stdout
↓
[Fluent Bit 사이드카]
↓ 라우팅
├─ CloudWatch Logs
├─ Kinesis Firehose → S3
├─ Datadog
├─ OpenSearch
└─ Splunk
- 다중 백엔드
- 필터링 · 파싱 · 변환 가능
- 운영 로그와 분석용 로그를 다른 곳으로 분리 가능
3. 언제 FireLens가 필요한가
1. 로그를 여러 백엔드로
- 운영용: CloudWatch
- 장기 보관: S3
- 분석: OpenSearch / Datadog
2. 사전 처리가 필요
- 민감 정보 마스킹
- 필드 추가/삭제
- 다른 포맷으로 변환
3. 비용 절감
- 디버그 로그는 S3 (Glacier) 로 직행
- 운영 로그만 CloudWatch (비싸지만 검색 빠름)
4. 멀티 클라우드 표준
- Fluent Bit은 AWS · GCP · Azure 어디서나 동일
4. 사이드카 패턴
Task 안에 두 개의 컨테이너 가 함께 돈다.
Task
├─ app (essential = true)
└─ log_router (essential = false, Fluent Bit)
- app 죽으면 Task 죽음
- log_router 죽어도 app은 살아 있음 (
essential = false)
5. 로그 형식 — JSON 한 줄
어떤 드라이버를 쓰든 가장 중요한 건 JSON 한 줄로 출력 하는 것이다.
{"ts":"2026-01-01T10:00:00Z","level":"info","trace_id":"abc","msg":"order created","order_id":"o-1","user_id":"u-1"}
- CloudWatch Logs Insights가 자동 인식
- Fluent Bit · OpenSearch 모두 자연스럽게 처리
Logger 라이브러리에서 JSON 출력을 기본으로 설정
6. EMF — 로그 한 줄로 메트릭까지
CloudWatch Embedded Metric Format 을 쓰면
로그 출력만으로 메트릭이 자동 생성된다.
{
"_aws": {
"CloudWatchMetrics": [{
"Namespace": "MyApp/Orders",
"Dimensions": [["Service"]],
"Metrics": [{ "Name": "OrdersCreated", "Unit": "Count" }]
}]
},
"Service": "orders",
"OrdersCreated": 1
}
- PutMetricData 호출 안 함 (비용 절감)
- 로그가 곧 메트릭의 원본
- 라이브러리 (
aws-embedded-metrics) 사용이 일반적
7. 우리 서비스에서
단순 서비스 (대다수)
ECS Task → awslogs → /ecs/<service-name>
핵심 서비스 (payments · audit)
ECS Task → FireLens (Fluent Bit 사이드카)
├─ CloudWatch Logs (운영 검색)
└─ Kinesis Firehose → S3 (Glacier, 장기 감사)
용도가 단순하면 awslogs, 복잡한 라우팅이 필요하면 FireLens.
8. 직접 확인해보기 — Task Definition (FireLens 예)
{
"family": "orders",
"containerDefinitions": [
{
"name": "app",
"image": "<ecr>/orders:v1",
"essential": true,
"logConfiguration": {
"logDriver": "awsfirelens"
}
},
{
"name": "log_router",
"image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:stable",
"essential": false,
"firelensConfiguration": {
"type": "fluentbit",
"options": { "enable-ecs-log-metadata": "true" }
},
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/firelens",
"awslogs-region": "ap-northeast-2",
"awslogs-stream-prefix": "router"
}
}
}
]
}
app은 awsfirelens 드라이버로 보내고, 라우터의 출력만 awslogs.
9. 코드로는 이렇게 생겼다 — Terraform (FireLens Task)
resource "aws_ecs_task_definition" "orders" {
family = "orders"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "512"
memory = "1024"
execution_role_arn = aws_iam_role.task_execution.arn
task_role_arn = aws_iam_role.orders_task.arn
container_definitions = jsonencode([
{
name = "app"
image = "${aws_ecr_repository.orders.repository_url}:v1"
essential = true
portMappings = [{ containerPort = 8080 }]
logConfiguration = { logDriver = "awsfirelens" }
},
{
name = "log_router"
image = "public.ecr.aws/aws-observability/aws-for-fluent-bit:stable"
essential = false
firelensConfiguration = {
type = "fluentbit"
options = { enable-ecs-log-metadata = "true" }
}
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = "/ecs/firelens"
awslogs-region = "ap-northeast-2"
awslogs-stream-prefix = "router"
}
}
}
])
}
Fluent Bit의 라우팅 규칙은 별도 ConfigMap (또는 S3) 파일로 둔다.
10. 이렇게 쓰면 망한다 — 안티패턴
안티패턴 1. 컨테이너 안에 파일로 로그를 쓴다
컨테이너 죽으면 사라진다.
항상 stdout / stderr
안티패턴 2. 한 줄 로그가 여러 줄에 걸친다 (stack trace 등)
파서가 깨진다.
멀티라인은 한 JSON 문자열로 묶거나 라우터에서 자동 합치기 설정
안티패턴 3. FireLens를 모든 서비스에 적용
단순한 서비스에 사이드카는 오버헤드.
단순함이 답인 곳은 그대로 awslogs
안티패턴 4. 로깅 사이드카에 충분한 자원을 안 준다
Fluent Bit이 메모리 부족으로 죽으면 로그 손실.
11. 한 줄로 정리
컨테이너 로그는 stdout으로, awslogs (단순) 또는 FireLens (다중/변환) 로 보낸다
JSON 한 줄과 trace_id 가 핵심이다
12. 이 장의 핵심 정리
- 컨테이너 로그는 stdout/stderr 가 표준.
- ECS는 awslogs 드라이버로 CloudWatch에 바로 보낼 수 있다.
- FireLens는 다중 백엔드 · 변환 · 비용 최적화에 강하다.
- 로그는 JSON 한 줄로, trace_id · user_id 같은 필드를 포함.
- EMF로 로그가 곧 메트릭이 되는 패턴이 가능하다.
- 단순 서비스는 awslogs, 복잡한 라우팅은 FireLens.